﻿using Microsoft.Graphics.Canvas;
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Diagnostics;

using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel.Calls;
using Windows.Devices.PointOfService;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using 纸片人.AttributeUsageFloder;

// https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板

namespace 纸片人.PageViews
{
    [LastModified("2022-5-12", "create SphericalizationPage.xaml")]
    [PageType("cpu", "膨胀挤压")]
    /// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class SphericalizationPage : Page
    {

        int width, height;
        double raduis;
        Point center;
        double oa;




        Color[] colors;
        Color[] tempColors;
        WriteableBitmap wb;


        CanvasDevice device = new CanvasDevice();
        public SphericalizationPage()
        {
            this.InitializeComponent();
        }


        void initialParse()
        {

            center = new Point(width / 2, height / 2);
            raduis = Math.Min(width, height) / 2;





        }



        private void sphericalization_Click(object sender, RoutedEventArgs e)
        {





            //colors.CopyTo(tempColors, 0);

            int cx = width / 2;
            int cy = height / 2;
            int r = height / 2;
            CPointGloble m_ptGloble = new CPointGloble();
            m_ptGloble.m_R = height / 2;
            m_ptGloble.ptCenter = new Point(width / 2, height / 2);
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    m_ptGloble.ptOrigin = new Point(x, y);
                    if (m_ptGloble.PIsInRound())
                    {
                        double a = x - cx;
                        double b = y - cy;


                        double x2, y2;
                        x2 = x - cx;
                        y2 = y - cy;
                        double d = Math.Sqrt(1.0 * x2 * x2 + 1.0 * y2 * y2);



                        double length = 0.0f;       //点从平面沿球径投影到球面,再垂直投影到平面与圆心的距离
                        length = Math.Sin(Math.PI * d / r / 2.0) * r;

                        double angle = 0;


                        double xx = x - cx;
                        double yy = y - cy;

                        if (xx == 0)
                        {
                            if (yy > 0)
                            {
                                angle = Math.PI / 2.0;
                            }
                            else
                            {
                                angle = -Math.PI / 2.0;
                            }
                        }
                        else if (xx > 0)
                        {
                            angle = Math.Atan((yy) / xx);
                        }
                        else
                        {
                            angle = Math.Atan((yy) / xx) + Math.PI;
                        }


                        int oy = (int)(Math.Sin(angle) * length + cy);
                        int ox = (int)(Math.Cos(angle) * length + cy);




                        ox = ox < 0 ? 0 : ox;
                        ox = ox > width - 1 ? width - 2 : ox;
                        oy = oy < 0 ? 0 : oy;
                        oy = height - 1 < oy ? height - 2 : oy;


                        tempColors[x + y * width] = colors[ox + oy * width];


                    }
                    else
                    {
                        tempColors[x + y * width] = colors[x + y * width];

                    }

                }
            }







            initialImage();

        }

        void initialImage()
        {
            using (var b = CanvasBitmap.CreateFromColors(device, tempColors, width, height))
            {



                byte[] currentByte = b.GetPixelBytes();
                currentByte.CopyTo(wb.PixelBuffer);
                wb.Invalidate();
                image1.Source = wb;

                b.Dispose();
            }
        }







        byte[] byteColors;


        private void slider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
        {

        }

        private void sphericalization_Click2(object sender, RoutedEventArgs ee)
        {


            double R = Math.Max(width, height) / 2;
            double a = height / 2;
            double b = width / 2;
            double e = a / b;
            double pi = Math.PI;
            double K = pi / 2;

            double alpha = 1.0;

            tempColors = new Color[colors.Length];
            colors.CopyTo(tempColors, 0);

            for (int i = 1; i < height; i++)
            {


                for (int j = 1; j < width; j++)
                {


                    double y0 = height / 2 - i;
                    double x0 = j - width / 2;
                    double Dis = x0 * x0 + y0 * y0;
                    if (Dis < R * R)
                    {

                        double beta = 0;

                        if (x0 <= 0)
                        {

                            if (x0 < 0)
                            {
                                beta = Math.Atan(y0 / x0) + Math.PI;
                                double angle = beta * 180 / pi;

                            }
                            else
                            {
                                beta = Math.Atan(y0 / x0);
                                double angle = beta * 180 / pi;
                            }
                        }
                        else if (x0 > 0)
                        {

                            if (y0 > 0)
                            {
                                double sin = Math.Abs(y0) / Math.Sqrt(Dis);

                                beta = Math.Asin(sin) * 180 / pi;

                                beta = beta * Math.PI / 180;
                            }
                            else
                            {
                                double sin = y0 / Math.Sqrt(Dis);

                                beta = Math.Asin(sin) * 180 / pi;

                                beta = beta * Math.PI / 180;
                            }




                        }




                        double radius = Math.Asin(Math.Sqrt(Dis) / R) * R / K;
                        radius = (Math.Sqrt(Dis) - radius) * (1 - alpha) + radius;
                        double x = radius * Math.Cos(beta);
                        double y = radius * Math.Sin(beta);
                        x = width / 2 + x;
                        y = height / 2 - y;
                        if (x > 1 && x < width && y > 1 && y < height)
                        {


                            int x1 = (int)x;
                            int y1 = (int)y;

                            tempColors[i * width + j] = colors[x1 + width * y1];

                        }
                    }
                }
            }


            initialImage();


        }

        private void extrusion_Click(object sender, RoutedEventArgs e)
        {

            int R = height / 2;
            double pi = Math.PI;
            double K = pi / 2;
            double alpha = 1;
            for (int i = 0; i < height; i++)
            {


                for (int j = 0; j < width; j++)
                {


                    double y0 = (height + 1) / 2 - i;
                    double x0 = j - (width + 1) / 2;
                    double Dis = x0 * x0 + y0 * y0;
                    double radius0 = Math.Sqrt(Dis);
                    if (Dis < R * R)
                    {

                        double beta = 0;

                        if (x0 <= 0)
                        {

                            if (x0 < 0)
                            {
                                beta = Math.Atan(y0 / x0) + Math.PI;



                            }
                            else
                            {
                                beta = Math.Atan(y0 / x0);

                            }
                        }
                        else if (x0 > 0)
                        {

                            if (y0 > 0)
                            {
                                double sin = Math.Abs(y0) / Math.Sqrt(Dis);

                                beta = Math.Asin(sin) * 180 / pi;
                                beta = beta * Math.PI / 180;
                            }
                            else
                            {
                                double sin = y0 / Math.Sqrt(Dis);

                                beta = Math.Asin(sin) * 180 / pi;
                                beta = beta * Math.PI / 180;
                            }




                        }




                        double theta = radius0 / R * K;
                        double radius = R * Math.Sin(theta);
                        radius = (radius - radius0) * alpha + radius0;
                        double x = radius * Math.Cos(beta);
                        double y = radius * Math.Sin(beta);
                        x = width / 2 + x;
                        y = height / 2 - y;

                        int x1 = (int)x;
                        int y1 = (int)y;

                        x1 = x1 < 0 ? 0 : x1;
                        x1 = x1 > width - 1 ? width - 2 : x1;
                        y1 = y1 < 0 ? 0 : y1;
                        y1 = y1 > width - 1 ? width - 2 : y1;

                        tempColors[j + i * width] = colors[x1 + width * y1];

                    }
                }
            }



            initialImage();

        }

        private void swell_Click(object sender, RoutedEventArgs e)
        {
            double cy = height / 2;
            double cx = width / 2;
            double swellValue = 1;

            int r = Math.Min(width, height) / 2 / 2;

            Color[] cacheColor = new Color[tempColors.Length];
            tempColors.CopyTo(cacheColor, 0);


            for (int y = 0; y < height; y++)
            {

                for (int x = 0; x < width; x++)
                {
                    double yy = y - cy;
                    double xx = x - cx;
                    double distance = Math.Sqrt(xx * xx + yy * yy);
                    if (distance <= r)
                    {
                        double minR = distance / r * swellValue * 1.25;

                        double d = distance;
                        distance = distance + swellValue;




                        distance = distance > r ? r : distance;
                        distance = distance < 0 ? 0 : distance;
                        double angle = 0;

                        if (xx == 0)
                        {
                            if (yy > 0)
                            {
                                angle = Math.PI / 2.0;
                            }
                            else
                            {
                                angle = -Math.PI / 2.0;
                            }
                        }
                        else if (xx > 0)
                        {

                            angle = Math.Atan((yy) / xx);

                        }
                        else
                        {
                            angle = Math.Atan((yy) / xx) + Math.PI;
                        }







                        double X = (int)(distance * Math.Cos(angle) + cx);
                        double Y = (int)(distance * Math.Sin(angle) + cy);

                        if (X >= width - 1) X = width - 2;
                        if (X < 0) X = 0;
                        if (Y >= height - 1) Y = height - 2;
                        if (Y < 0) Y = 0;

                        int index = (int)(Y * width + X);
                        tempColors[x + y * width] = Function.BiLinear(X, Y, cacheColor, width, height);
                    }





                }
            }

            initialImage();
        }

        private void extrusion2_Click(object sender, RoutedEventArgs ee)
        {

            double cenX = width / 2;
            double cenY = height / 2;
            double radius = Math.Min(cenX, cenY)/2;
            double radius2 = radius * radius;
            double pi = Math.PI;
            double amount = 0.75;
            double angle = pi / 4;
            for (int ii = 1; ii < height; ii++)
            {



                for (int jj = 1; jj < width; jj++)
                {


                    double dx = jj - cenX;
                    double dy = ii - cenY;

                    double distance = dx * dx + dy * dy;

                    double x = 0, y = 0;

                    if (distance > radius2 || distance == 0)
                    {


                        x = jj;
                        y = ii;
                    }
                    else
                    {

                        double d = Math.Sqrt(distance / radius2);
                        double t = Math.Pow(Math.Sin(pi * 0.5 * d), -amount);

                        dx = dx * t;
                        dy = dy * t;

                        double e = 1 - d;
                        double a = angle * e * e;

                        double s = Math.Sin(a);
                        double c = Math.Cos(a);

                        x = cenX + c * dx - s * dy;
                        y = cenY + s * dx + c * dy;
                    }

                    if (x <= 1) x = 1;
                    if (x >= width) x = width - 1;
                    if (y >= height) y = height - 1;
                    if (y < 1) y = 1;

                    tempColors[jj + ii * width] = Function.BiLinear(x, y, colors, width, height);



                }
            }

            initialImage();
        }

        private async void selectImage_Click(object sender, RoutedEventArgs e)
        {




            FileOpenPicker picker = new FileOpenPicker();
            picker.FileTypeFilter.Add(".jpg");
            picker.FileTypeFilter.Add(".png");

            StorageFile file = await picker.PickSingleFileAsync();

            CanvasBitmap bitmap = await CanvasBitmap.LoadAsync(device, await file.OpenReadAsync());

            byteColors = bitmap.GetPixelBytes();

            width = (int)bitmap.Size.Width;
            height = (int)bitmap.Size.Height;



            colors = bitmap.GetPixelColors();
            tempColors = new Color[colors.Length];
            colors.CopyTo(tempColors, 0);
            wb = new WriteableBitmap(width, height);


            WriteableBitmap bi = new WriteableBitmap(width, height);

            initialParse();
            bitmap.GetPixelBytes().CopyTo(bi.PixelBuffer);

            image.Source = bi;
            bitmap.Dispose();


        }
    }


    public class CPointGloble
    {

        /// <summary>
        /// 球面半径
        /// </summary>
        public double m_R;
        /// <summary>
        /// 球面化的点
        /// </summary>
        public Point ptOrigin;
        /// <summary>
        /// 球面化的圆的圆心
        /// </summary>
        public Point ptCenter;

        public double x = -1;
        public double y = -1;

        /// <summary>
        /// 计算点到点的距离
        /// </summary>
        /// <param name="pt1"></param>
        /// <param name="pt2"></param>
        /// <returns></returns>
        public double PtoPDistance(Point pt1, Point pt2)
        {

            double x, y;
            x = pt1.X - pt2.X;
            y = pt1.Y - pt2.Y;
            return Math.Sqrt(1.0 * x * x + 1.0 * y * y);
        }

        /// <summary>
        /// 是否在圆内
        /// </summary>
        /// <returns></returns>
        public bool PIsInRound()
        {
            this.D = PtoPDistance(ptOrigin, ptCenter);
            return D - m_R <= 0;
        }

        /// <summary>
        /// 点到圆心的距离
        /// </summary>
        public double D;


        /// <summary>
        /// 将点转化成为球面投影在面上的点
        /// </summary>
        /// <returns></returns>
        public Point PtToGloble()
        {
            double length = 0.0f;       //点从平面沿球径投影到球面,再垂直投影到平面与圆心的距离



            length = Math.Sin(Math.PI * D / m_R / 2.0) * m_R;

            //double angle = GetAngleFromPt(ptCenter, ptOrigin);

            double angle = 0;


            double xx = ptOrigin.X - ptCenter.X;
            double yy = ptOrigin.Y - ptCenter.Y;

            if (xx == 0)
            {
                if (yy > 0)
                {
                    angle = Math.PI / 2.0;
                }
                else
                {
                    angle = -Math.PI / 2.0;
                }
            }
            else if (xx > 0)
            {
                angle = Math.Atan((yy) / xx);
            }
            else
            {
                angle = Math.Atan((yy) / xx) + Math.PI;
            }




            //Point pt = GetPtFromPolar(length, angle);

            double y1 = Math.Sin(angle) * length;
            double x1 = Math.Cos(angle) * length;

            y1 += y1 > 0 ? 0.5f : -0.5f;
            x1 += x1 > 0 ? 0.5f : -0.5f;


            this.x = x1 + ptCenter.X;
            this.y = y1 + ptCenter.Y;
            //pt = PointAdd(pt, ptCenter);
            return new Point();
        }

        /// <summary>
        /// 获取两点链接线于x轴的夹角
        /// </summary>
        /// <param name="ptStart"></param>
        /// <param name="ptEnd"></param>
        /// <returns></returns>
        public double GetAngleFromPt(Point ptStart, Point ptEnd)
        {


            double x = ptEnd.X - ptStart.X;
            double y = ptEnd.Y - ptStart.Y;

            if (x == 0)
            {
                if (y > 0)
                {
                    return Math.PI / 2.0;
                }
                else
                {
                    return -Math.PI / 2.0;
                }
            }
            else if (x > 0)
            {
                return Math.Atan((y) / x);
            }
            else
            {
                return Math.Atan((y) / x) + Math.PI;
            }
        }

        /// <summary>
        /// 极坐标转换为点的坐标
        /// </summary>
        /// <param name="length"></param>
        /// <param name="angle"></param>
        /// <returns></returns>
        public Point GetPtFromPolar(double length, double angle)
        {
            double y = Math.Sin(angle) * length;
            double x = Math.Cos(angle) * length;

            y += y > 0 ? 0.5f : -0.5f;
            x += x > 0 ? 0.5f : -0.5f;

            return new Point((int)(x), (int)(y));
        }


        /// <summary>
        /// 两点相加
        /// </summary>
        /// <param name="pt1"></param>
        /// <param name="pt2"></param>
        /// <returns></returns>
        public Point PointAdd(Point pt1, Point pt2)
        {
            return new Point(pt1.X + pt2.X, pt1.Y + pt2.Y);
        }

    }

}
